#include "OBJMesh.h"
#include <iostream>
#include "ReportError.h"
#include "StringUtil.h"
#include <assert.h>

#include <glut.h>

OBJMesh::OBJMesh()
{
	m_faces.reserve(1000);
	m_coords.reserve(1000);
	m_UV.reserve(1000);
	m_Normals.reserve(1000);
	m_MinX = m_MaxX = 0;
	m_MinY = m_MaxY = 0;
	m_MinZ = m_MaxZ = 0;
}

bool OBJMesh::Load(const std::string &FileName)
{
	const int v = Hashed("v");
	const int vn = Hashed("vn");
	const int vt = Hashed("vt");
	const int f = Hashed("f");
	const int mtllib = Hashed("mtllib");
	const int polycount = Hashed("polycount");
	const int vertexcount = Hashed("vertexcount");
	const int uvcount = Hashed("uvcount");
	const int tag = Hashed("Tag");
	const int texture = Hashed("Texture");

	File OBJFile;
	if(!OBJFile.OpenRead(FileName))
	{
		return false;
	}

	while(OBJFile.GetString(&m_CurrentLine))
	{
		Strings strs = Split(m_CurrentLine, ' ');
		int HashedLine = Hashed(strs[0]);

		//Read In Polycount
		if (HashedLine == polycount)
		{
			m_faces.reserve(std::atoi(strs[1].c_str()));
			continue;
		}

		//Read In Vertexcount
		if (HashedLine == vertexcount)
		{
			m_coords.reserve(std::atoi(strs[1].c_str()));
			continue;
		}

		//Read In UVcount
		if (HashedLine == uvcount)
		{
			m_UV.reserve(std::atoi(strs[1].c_str()));
			continue;
		}

		//Read In Vertex Position
		if (HashedLine == v)
		{
			Vec3f Vertex = ToVec3(strs);
			m_coords.push_back(Vertex);
			continue;
		}

		//Read In Normal Information
		if (HashedLine == vn)
		{
			m_Normals.push_back(ToVec3(strs));
			continue;
		}

		//Read In UV Information
		if (HashedLine == vt)
		{
			m_UV.push_back(ToVec2(strs));
			continue;
		}

		//Read In Face Information
		if (HashedLine == f)
		{
			Face TempFace;
			assert(strs.size() == 4);
			for(int i = 1; i < 4; ++i)
			{
				Strings faceStr = Split(strs[i], '/');
				assert(faceStr.size() == 3);

				TempFace.m_pointIndex[i-1] = std::atoi(faceStr[0].c_str());
				TempFace.m_uvIndex[i-1] = std::atoi(faceStr[1].c_str());
				TempFace.m_normalIndex[i-1] = std::atoi(faceStr[2].c_str());
			}

			m_faces.push_back(TempFace);
			continue;
		}

		//Read In Tag for TextureManager
		if (HashedLine == tag)
		{
			m_Tag = strs[1];
			continue;
		}
		//Read In Material File
		if (HashedLine == mtllib)
		{
			LoadMTL("Assets/Textures/" + strs[1]);
			continue;
		}

		if(HashedLine == texture)
		{
			TheTextureManager::Instance()->LoadTexture(strs[1], m_Tag);
			continue;
		}
	}

	for(int i = 0; i < m_faces.size(); i++)
	{
		for (int j = 0; j < 3; ++j)
		{
			ThePositionVec.push_back(m_coords[m_faces[i].m_pointIndex[j]-1]);
			TheNormalVec.push_back(m_Normals[m_faces[i].m_normalIndex[j]-1]);
			TheUVVec.push_back(m_UV[m_faces[i].m_uvIndex[j]-1]);
		}
	}


	m_Id = glGenLists(1);
	glNewList(m_Id, GL_COMPILE);
	glBegin(GL_TRIANGLES);

	for(int i = 0; i < m_faces.size(); i++)
	{
		for (int j = 0; j < 3; ++j)
		{
			Vec3f position = m_coords[m_faces[i].m_pointIndex[j]-1];
			Vec3f normal = m_Normals[m_faces[i].m_normalIndex[j]-1];
			Vec2f uv = m_UV[m_faces[i].m_uvIndex[j]-1];

			glNormal3f(normal.m_x,normal.m_y,normal.m_z);
			glTexCoord2f(-uv.x, -uv.y);
			glVertex3f(position.m_x, position.m_y, position.m_z);
		}
	}

	glEnd();
	glEndList();


}


void OBJMesh::DrawUsingVertexArray()
{
	//TheTextureManager::Instance()->GetTexture(m_Tag);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, &ThePositionVec[0]);
	glNormalPointer(GL_FLOAT, 0, &TheNormalVec[0]);
	glTexCoordPointer(2, GL_FLOAT, 0, &TheUVVec[0]);
	glDrawArrays(GL_TRIANGLES, 0 , sizeof(Vec3f) * ThePositionVec.size());
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void OBJMesh::Draw()
{
	if(m_Tag.size() > 0)
	{
		TheTextureManager::Instance()->GetTexture(m_Tag);
	}
	glCallList(m_Id);
	TheTextureManager::Instance()->UnBind();
}


void OBJMesh::DrawWireFrame()
{
	TheTextureManager::Instance()->GetTexture(m_Tag);

	glBegin(GL_LINE_STRIP);

	for(int i = 0; i < m_faces.size(); i++)
	{
		for (int j = 0; j < 3; ++j)
		{
			Vec3f position = m_coords[m_faces[i].m_pointIndex[j]-1];
			Vec3f normal = m_Normals[m_faces[i].m_normalIndex[j]-1];
			Vec2f uv = m_UV[m_faces[i].m_uvIndex[j]-1];

			glNormal3f(normal.m_x,normal.m_y,normal.m_z);
			glTexCoord2f(-uv.x, -uv.y);
			glVertex3f(position.m_x, position.m_y, position.m_z);
		}
	}

	glEnd();
}

void OBJMesh::LoadMTL(const std::string& Filename)
{
	File mtlFile;
	mtlFile.OpenRead(Filename);
	while(mtlFile.GetString(&m_CurrentLine))
	{
		Strings strs = Split(m_CurrentLine, ' ');
		if(strs[0] == "map_Kd")
		{
			TheTextureManager::Instance()->LoadTexture("Assets/" + strs[1], m_Tag);
		}


	}

}

void OBJMesh::Clear()
{
	m_coords.clear();
	m_Normals.clear();
	m_UV.clear();
	m_faces.clear();
	ThePositionVec.clear();
	TheNormalVec.clear();
	TheUVVec.clear();
}

void OBJMesh::SetAnimationMesh(std::vector<Vec3f>& ModifiedMesh)
{
	TheAnimationPositionVec = ModifiedMesh;
}